
package redeS.mensagem;

import java.util.ArrayList;

import peersim.cdsim.CDState;
import peersim.config.Configuration;
import peersim.core.Control;
import peersim.core.Linkable;
import peersim.core.Network;
import redeS.visualizacao.P2PObserver;
import redeS.visualizacao.P2PObserverConstants;

/**
 * Classe de controle da arquitetura Gnutella.
 * Essa classe é responsável pela movimentação das mensagens entre os nós da rede.
 * @author thiago-pc
 */
public class GnuMessageControl implements Control {
	
	/**
	 * O parâmetro TTL (time-to-live) representa o número de vezes
	 * que uma mensagem será encaminhada na rede antes de ser excluída.
	 */
	public static final String PAR_TTL = "TTL";
	public static int TTL;
	private final int defaultTTL = 6;
	
	/**
	 * Número de arquivos atualmente na rede.
	 */
	public static int numberOfFiles = 0;
	
	private ArrayList<GnutellaMessage> messages;
	
	/**
	 * Número <b>médio</b> de Pings que cada nó enviará na simulação.
	 */
	public static final String PAR_PINGS = "pings_per_node";
	private final int defaultPings = 0;
	// probabilidade de um ping a cada ciclo
	private double pings;
	
	/**
	 * Número <b>médio</b> de Queries que cada nó enviará na simulação.
	 */
	public static final String PAR_QUERIES = "queries_per_node";
	private final int defaultQueries = 0;
	// probablidade de um query a cada ciclo
	private double queries;
	
	/**
	 * Popularidade máxima que um arquivo pode ter. 0 a 100.
	 */
	public static final String PAR_POPMQ = "file_max_popularity";
	private int popularity;
	
	// Outras requisições:
	// Protocolo da Arquitetura Gnutella: String "protocol"
	private int pid = -1;
	// Protocolo das Mensagens Gnutella: String "message_protocol"
	private int pidm = -1;
	
	public GnuMessageControl(String prefix){
		// Pega o valor do parâmetro TTL do arquivo de configuração.
		// Se o arquivo de configuração não tiver esse parâmetro ele será setado
		// com o valor padrão (veja o valor default na declaração da variável).
		TTL = Configuration.getInt(prefix + "." + PAR_TTL,defaultTTL);
		
		pings = Configuration.getInt(prefix + "." + PAR_PINGS,defaultPings) /
					(double)CDState.getEndTime();
		queries = Configuration.getInt(prefix + "." + PAR_QUERIES,defaultQueries) /
					(double)CDState.getEndTime();
		
		if(Configuration.contains(prefix + ".protocol") ){
			pid = Configuration.getPid(prefix + ".protocol");
		}
		if(pid<0){
			System.err.println("Especifique a string identificadora do protocolo Gnutella");
			System.exit(1);
		}
		
		if(Configuration.contains(prefix + ".message_protocol") ){
			pidm = Configuration.getPid(prefix + ".message_protocol");
		}
		if(pidm<0){
			System.err.println("Especifique a string identificadora do protocolo de " +
					"mensagens Gnutella");
			System.exit(1);
		}
		popularity = Configuration.getInt(prefix + "." + PAR_POPMQ,0);
		messages = new ArrayList<GnutellaMessage>();
	}
	
	@Override
	public boolean execute() {
		
		Linkable lnkProtocol;
		GnuMessageProtocol gnuProtocol;
		
		//para cada nó na rede...
		for(int i=0; i<Network.size(); i++){
			//pega os protocolos deles
			lnkProtocol = (Linkable)Network.get(i).getProtocol(pid);
			gnuProtocol = (GnuMessageProtocol)Network.get(i).getProtocol(pidm);
			ArrayList<GnutellaMessage> tempNewMessages;
			
			//processa ping
			if(Math.random() < pings){
				
				Ping newPing = new Ping(CDState.r.nextLong(),TTL);
				gnuProtocol.addRequest(newPing, i);
				messages.add(newPing);
			}
			
			//processa query
			if(Math.random() < queries){
				
				int pop = popularity>0? CDState.r.nextInt() % popularity : 0; 
				Query newQuery = new Query(CDState.r.nextLong(),TTL,pop/100.00);
				gnuProtocol.addRequest(newQuery, i);
				messages.add(newQuery);
			}
			
			//pega novas mensagens
			tempNewMessages = gnuProtocol.getNewMessages();
			for(int j=0; j<tempNewMessages.size(); j++){
				messages.add(tempNewMessages.get(j));
			}
		}
		//decrementa os ttls das mensagens
		for(int i=0; i<messages.size(); i++){
			if(messages.get(i).getTTL() > 0){
				messages.get(i).TTLDecrement();
			}
			else{
				messages.remove(i);
				i--;
			}
		}
		
		// /*******************/ Classe observadora /*******************/
		P2PObserver.setData(P2PObserverConstants.TOTALDIFFERENTMESSAGES,
				(float)messages.size(), null);
		// /************************************************************/
		
		return false;
	}

}
